﻿using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Internal.KuiHuaBaoDian.Services.Asset;
using KuiHuaBaoDian.Services.Asset;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Assertions;
using UnityEngine.ResourceManagement.AsyncOperations;

namespace KuiHuaBaoDian.Services.Asset {

    /// <summary>
    /// 资产服务
    /// </summary>
    public static class AssetService {

        /// <summary>
        /// 单例 
        /// </summary>
        public static IAssetService Instance => m_Instance ??= KHBD.TypeMap.Instantiate<IAssetService>();
        private static IAssetService m_Instance;

        /// <summary>
        /// 加载
        /// </summary>
        /// <typeparam name="T">资产类型</typeparam>
        /// <param name="request">加载请求</param>
        /// <param name="onComplete">当加载完成时的回调</param>
        /// <returns>异步任务</returns>
        public static UniTask<AssetHandle<T>> Load<T>(string key, Action<AssetHandle<T>> onComplete = null) where T : UnityEngine.Object => Instance.Load(AssetRequest.Instantiate(key), onComplete);

        /// <summary>
        ///  加载并实例化<see cref="GameObject"/>
        /// </summary>
        /// <param name="key">资产Key</param>
        /// <returns>异步任务</returns>
        public static UniTask<GameObject> Instantiate(string key) => Instance.Instantiate(AssetRequest.Instantiate(key));

        /// <summary>
        ///  加载并实例化<see cref="GameObject"/>
        /// </summary>
        /// <param name="key">资产Key</param>
        /// <param name="assetInstantiateParent">可以获取父节点的<see cref="IAssetInstantiateParent"/>实现</param>
        /// <returns>异步任务</returns>
        public static UniTask<GameObject> Instantiate(string key, IAssetInstantiateParent assetInstantiateParent) => Instance.Instantiate(AssetRequest.Instantiate(key), assetInstantiateParent);

        /// <summary>
        /// 加载并实例化<see cref="GameObject"/>
        /// </summary>
        /// <param name="key">资产Key</param>
        /// <param name="parent">父节点</param>
        /// <returns>异步任务</returns>
        public static UniTask<GameObject> Instantiate(string key, Transform parent) => Instance.Instantiate(AssetRequest.Instantiate(key), parent);

        /// <summary>
        /// 仅下载预制体，不保留在运行时内存中
        /// </summary>
        /// <param name="key">资产Key</param>
        /// <returns>是否下载完成，异步任务</returns>
        public static UniTask<bool> DownloadPrefab(string key) => Instance.Download<GameObject>(AssetRequest.Instantiate(key));

        /// <summary>
        /// 仅下载，不保留在运行时内存中，Prefab预制体会有错误释放的情况，请使用<see cref="AssetService.Download{T}(IAssetRequest, Action{bool})"/>下载。
        /// </summary>
        /// <param name="key">资产Key</param>
        /// <returns>是否下载完成，异步任务</returns>
        public static UniTask<bool> DownloadDependencies(string key) => Instance.DownloadDependencies(AssetRequest.Instantiate(key));

        /// <summary>
        /// 释放资产
        /// </summary>
        /// <param name="handle">资产句柄</param>
        public static void Release(AssetHandle handle) => Instance.Release(handle);

        /// <summary>
        /// 释放直接通过<see cref="IAssetService.Instantiate(IAssetRequest, Transform, bool, Action{GameObject}, Action{GameObject})"/>实例化的<see cref="GameObject"/>对象
        /// </summary>
        /// <param name="gameObject"></param>
        public static void Release(GameObject gameObject) => Instance.Release(gameObject);
    }

    /// <summary>
    /// 资产服务
    /// </summary>
    public interface IAssetService {

        /// <summary>
        /// 仅下载，不保留在运行时内存中
        /// </summary>
        /// <typeparam name="T">资产类型</typeparam>
        /// <param name="request">加载请求</param>
        /// <param name="onComplete">当下载完成回调</param>
        /// <returns>是否下载完成，异步任务</returns>
        UniTask<bool> Download<T>(IAssetRequest request, Action<bool> onComplete = null);

        /// <summary>
        /// 仅下载，不保留在运行时内存中，Prefab预制体会有错误释放的情况，请使用<see cref="IAssetService.Download{T}(IAssetRequest, Action{bool})"/>下载。
        /// </summary>
        /// <param name="request">加载请求</param>
        /// <param name="onComplete">当下载完成回调</param>
        /// <returns>是否下载完成，异步任务</returns>
        UniTask<bool> DownloadDependencies(IAssetRequest request, Action<bool> onComplete = null);

        /// <summary>
        /// 加载
        /// </summary>
        /// <typeparam name="T">资产类型</typeparam>
        /// <param name="request">加载请求</param>
        /// <param name="onComplete">当加载完成时的回调</param>
        /// <param name="cacheGroup">缓存组名称</param>
        /// <returns>异步任务</returns>
        UniTask<AssetHandle<T>> Load<T>(IAssetRequest request, Action<AssetHandle<T>> onComplete = null, string cacheGroup = null) where T : UnityEngine.Object;

        /// <summary>
        /// 加载并实例化<see cref="GameObject"/>
        /// </summary>
        /// <param name="request">加载请求</param>
        /// <param name="parent">父节点</param>
        /// <param name="isActive">是否初始处于激活状态</param>
        /// <param name="onComplete">当加载完成时的回调</param>
        /// <param name="cacheGroup">缓存组名称</param>
        /// <returns>异步任务</returns>
        UniTask<GameObject> Instantiate(IAssetRequest request, Transform parent, bool isActive = true, Action<GameObject> onComplete = null, string cacheGroup = null);

        /// <summary>
        ///  加载并实例化<see cref="GameObject"/>
        /// </summary>
        /// <param name="request">加载请求</param>
        /// <param name="assetInstantiateParent">可以获取父节点的<see cref="IAssetInstantiateParent"/>实现</param>
        /// <param name="isActive">是否初始处于激活状态</param>
        /// <param name="onComplete">当加载完成时的回调</param>
        /// <param name="cacheGroup">缓存组名称</param>
        /// <returns>异步任务</returns>
        UniTask<GameObject> Instantiate(IAssetRequest request, IAssetInstantiateParent assetInstantiateParent, bool isActive = true, Action<GameObject> onComplete = null, string cacheGroup = null);

        /// <summary>
        ///  加载并实例化<see cref="GameObject"/>
        /// </summary>
        /// <param name="request">加载请求</param>
        /// <param name="isActive">是否初始处于激活状态</param>
        /// <param name="onComplete">当加载完成时的回调</param>
        /// <param name="cacheGroup">缓存组名称</param>
        /// <returns>异步任务</returns>
        UniTask<GameObject> Instantiate(IAssetRequest request, bool isActive = true, Action<GameObject> onComplete = null, string cacheGroup = null);

        /// <summary>
        /// 释放资产
        /// </summary>
        /// <param name="handle">资产句柄</param>
        void Release(AssetHandle handle);

        /// <summary>
        /// 释放直接通过<see cref="Instantiate(IAssetRequest, Transform, bool, Action{GameObject}, Action{GameObject})"/>实例化的<see cref="GameObject"/>对象
        /// </summary>
        /// <param name="gameObject"></param>
        void Release(GameObject gameObject);

        /// <summary>
        /// 清除缓存
        /// </summary>
        /// <param name="cacheGroup">缓存组名称</param>
        void ClearCache(string cacheGroup);
    }

    [Implement(typeof(IAssetService))]
    internal sealed partial class KHBD_AssetService : IAssetService, IAssetServiceInternal, IService {

        public KHBD_AssetService() {
            var gameObject = new GameObject(nameof(AssetService));
            UnityEngine.Object.DontDestroyOnLoad(gameObject);
            gameObject.hideFlags = HideFlags.HideAndDontSave;
            m_CacheTransform = gameObject.transform;
        }

        private record Record(string Key, AsyncOperationHandle AsyncOperationHandle);

        private readonly IDictionary<AssetHandle, Record> m_Dictionary = new Dictionary<AssetHandle, Record>();
        private readonly IDictionary<GameObject, AsyncOperationHandle<GameObject>> m_InstantiatedDictionary = new Dictionary<GameObject, AsyncOperationHandle<GameObject>>();

        private readonly IDictionary<string, CacheGroup> m_CacheGroupDictionary = new Dictionary<string, CacheGroup>();

        private readonly IDictionary<AssetHandle, AssetHandle> m_CacheMap = new Dictionary<AssetHandle, AssetHandle>();
        private readonly IDictionary<AssetHandle, HashSet<AssetHandle>> m_CacheInstanceSetDictionary = new Dictionary<AssetHandle, HashSet<AssetHandle>>();
        private readonly IDictionary<string, AssetHandle> m_CacheDictionary = new Dictionary<string, AssetHandle>();

        private readonly IDictionary<GameObject, string> m_CacheGameObjectMap = new Dictionary<GameObject, string>();
        private readonly IDictionary<string, HashSet<GameObject>> m_CacheGameObjectInstanceDictionary = new Dictionary<string, HashSet<GameObject>>();
        private readonly IDictionary<string, GameObject> m_CacheGameObjectDictionary = new Dictionary<string, GameObject>();
        private readonly IDictionary<string, Stack<GameObject>> m_CacheGameObjectStackDictionary = new Dictionary<string, Stack<GameObject>>();

        private readonly Transform m_CacheTransform;

        void IService.OnApplicationQuit() {
            m_Dictionary.Clear();
            m_InstantiatedDictionary.Clear();

            foreach (var kv in m_CacheGroupDictionary) {
                kv.Value.KeyStateDictionary.Clear();
            }
            m_CacheGroupDictionary.Clear();

            m_CacheMap.Clear();
            foreach (var kv in m_CacheInstanceSetDictionary) {
                kv.Value.Clear();
            }
            m_CacheInstanceSetDictionary.Clear();
            m_CacheDictionary.Clear();

            m_CacheGameObjectMap.Clear();
            foreach (var kv in m_CacheGameObjectInstanceDictionary) {
                kv.Value.Clear();
            }
            m_CacheGameObjectInstanceDictionary.Clear();
            m_CacheGameObjectDictionary.Clear();

            foreach (var kv in m_CacheGameObjectStackDictionary) {
                kv.Value.Clear();
            }
            m_CacheGameObjectStackDictionary.Clear();
        }

        private void DisposeRequest(IAssetRequest request) {
            if (request is IDisposable disposable) {
                disposable.Dispose();
            }
        }

        public void ClearCache() {
            var keys = m_CacheGameObjectDictionary.Keys;
            foreach (var key in keys) {
                ClearCache(key);
            }
        }

        public void ClearCache(string cacheGroup) {
            if (string.IsNullOrWhiteSpace(cacheGroup)) {
                return;
            }
            if (m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group)) {
                m_CacheGroupDictionary.Remove(cacheGroup);
                if (group.IsGameObject) {
                    var keys = group.KeyStateDictionary.Keys.ToArray();
                    foreach (var key in keys) {
                        if (m_CacheGameObjectDictionary.TryGetValue(key, out var gameObject) && m_InstantiatedDictionary.TryGetValue(gameObject, out var handle)) {
                            m_CacheGameObjectDictionary.Remove(key);
                            m_InstantiatedDictionary.Remove(gameObject);
                            var instanceSet = m_CacheGameObjectInstanceDictionary[key];
                            m_CacheGameObjectInstanceDictionary.Remove(key);
                            foreach (var instance in instanceSet) {
                                m_CacheGameObjectMap.Remove(instance);
                                UnityEngine.Object.Destroy(instance);
                            }
                            if (m_CacheGameObjectStackDictionary.TryGetValue(key, out var stack)) {
                                foreach (var item in stack) {
                                    UnityEngine.Object.Destroy(item);
                                }
                                stack.Clear();
                            }
                            Addressables.Release(handle);
                        }
                        group.KeyStateDictionary[key] = CacheGroup.State.Failed;
                    }
                } else {
                    var keys = group.KeyStateDictionary.Keys.ToArray();
                    foreach (var key in keys) {
                        if (m_CacheDictionary.TryGetValue(key, out var handle) && m_Dictionary.TryGetValue(handle, out var record)) {
                            m_CacheDictionary.Remove(key);
                            m_Dictionary.Remove(handle);
                            var instanceSet = m_CacheInstanceSetDictionary[handle];
                            m_CacheInstanceSetDictionary.Remove(handle);
                            foreach (var instance in instanceSet) {
                                m_CacheMap.Remove(instance);
                            }
                            Addressables.Release(record.AsyncOperationHandle);
                        }
                        group.KeyStateDictionary[key] = CacheGroup.State.Failed;
                    }
                }
                (group as IDisposable).Dispose();
            }
        }

        public async UniTask<AssetHandle<T>> Load<T>(IAssetRequest request, Action<AssetHandle<T>> onComplete = null, string cacheGroup = null) where T : UnityEngine.Object {
            Assert.IsNotNull(request);

            request.DownloadProgress?.SetProgress(0);

            if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                onComplete?.Invoke(AssetHandle.None);
                DisposeRequest(request);
                return AssetHandle.None;
            }

            var key = Validate(request.Key);
            if (string.IsNullOrEmpty(key)) {
                Debug.LogWarning("请求空资产键");
                onComplete?.Invoke(AssetHandle.None);
                DisposeRequest(request);
                return AssetHandle.None;
            }

            var isRequireCache = !string.IsNullOrWhiteSpace(cacheGroup);
            while (isRequireCache && KHBD.Context.IsApplicationPlaying2) {
                var cache = GetHandleCache<T>(key);
                if (cache != AssetHandle.None) {
                    onComplete?.Invoke(cache);
                    DisposeRequest(request);
                    return cache;
                }
                if (isRequireCache) {
                    if (!m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group)) {
                        group = m_CacheGroupDictionary[cacheGroup] = new CacheGroup(false);
                    }
                    if (group.KeyStateDictionary.TryAdd(key, CacheGroup.State.Loading)) {
                        break;
                    } else {
                        if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                            onComplete?.Invoke(AssetHandle.None);
                            DisposeRequest(request);
                            return AssetHandle.None;
                        }
                        await KHBD.Context.WaitUntil(() => group.KeyStateDictionary[key] != CacheGroup.State.Loading, request.CancellationToken);
                        // 自己是取消的请求时
                        if (KHBD.Context.IsCancellationRequested(request.CancellationToken) || group.KeyStateDictionary[key] == CacheGroup.State.Failed) {
                            onComplete?.Invoke(AssetHandle.None);
                            DisposeRequest(request);
                            return AssetHandle.None;
                        } else if (group.KeyStateDictionary[key] == CacheGroup.State.Cancelled) { // 其它请求取消时
                            group.KeyStateDictionary[key] = CacheGroup.State.Loading;
                            break;
                        }
                    }
                } else {
                    break;
                }
            }

            var opHandle = Addressables.LoadAssetAsync<T>(key);
            var operateResult = await OperateHandle(request, opHandle);
            switch (operateResult) {
                case OperateResult.Succeed:
                    var handle = new AssetHandle<T> { GUID = Guid.NewGuid() };
                    var record = new Record(key, opHandle);
                    m_Dictionary.Add(handle, record);
                    if (isRequireCache) {
                        m_CacheDictionary[key] = handle;
                        m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group);
                        group.KeyStateDictionary[key] = CacheGroup.State.Completed;
                        var cache = GetHandleCache<T>(key);
                        onComplete?.Invoke(cache);
                        DisposeRequest(request);
                        return cache;
                    } else {
                        onComplete?.Invoke(handle);
                        DisposeRequest(request);
                        return handle;
                    }
                case OperateResult.Failed:
                    if (isRequireCache) {
                        if (m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group)) {
                            group.KeyStateDictionary[key] = CacheGroup.State.Failed;
                        }
                    }
                    onComplete?.Invoke(AssetHandle.None);
                    DisposeRequest(request);
                    return AssetHandle.None;
                case OperateResult.Cancelled:
                    if (isRequireCache) {
                        if (m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group)) {
                            group.KeyStateDictionary[key] = CacheGroup.State.Cancelled;
                        }
                    }
                    onComplete?.Invoke(AssetHandle.None);
                    DisposeRequest(request);
                    return AssetHandle.None;
                default:
                    throw new NotSupportedException();
            }
        }

        private AssetHandle<T> GetHandleCache<T>(string key) where T : UnityEngine.Object {
            if (m_CacheDictionary.TryGetValue(key, out var cache) && m_Dictionary.TryGetValue(cache, out _)) {
                var handle = new AssetHandle<T> { GUID = Guid.NewGuid() };
                m_CacheMap[handle] = cache;
                if (!m_CacheInstanceSetDictionary.TryGetValue(cache, out var instanceSet)) {
                    instanceSet = m_CacheInstanceSetDictionary[cache] = new();
                }
                instanceSet.Add(handle);
                return handle;
            }
            return AssetHandle.None;
        }

        public async UniTask<GameObject> Instantiate(IAssetRequest request, Transform parent, bool isActive = true, Action<GameObject> onComplete = null, string cacheGroup = null) {
            Assert.IsNotNull(request);

            request.DownloadProgress?.SetProgress(0);

            if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                onComplete?.Invoke(null);
                DisposeRequest(request);
                return null;
            }

            var key = Validate(request.Key);
            if (string.IsNullOrEmpty(key)) {
                Debug.LogWarning("请求空资产键");
                onComplete?.Invoke(null);
                DisposeRequest(request);
                return null;
            }

            var hasParent = parent != null;
            var originParent = parent;
            var isRequireCache = !string.IsNullOrWhiteSpace(cacheGroup);
            while (isRequireCache && KHBD.Context.IsApplicationPlaying2) {
                var cache = GetInstantiateCache(key, hasParent, parent, isActive);
                if (cache != null) {
                    onComplete?.Invoke(cache);
                    DisposeRequest(request);
                    return cache;
                }
                if (isRequireCache) {
                    if (!m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group)) {
                        group = m_CacheGroupDictionary[cacheGroup] = new CacheGroup(true);
                    }
                    if (group.KeyStateDictionary.TryAdd(key, CacheGroup.State.Loading)) {
                        parent = m_CacheTransform;
                        break;
                    } else {
                        if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                            onComplete?.Invoke(null);
                            DisposeRequest(request);
                            return null;
                        }
                        await KHBD.Context.WaitUntil(() => group.KeyStateDictionary[key] != CacheGroup.State.Loading, request.CancellationToken);
                        // 自己是取消的请求或失败时
                        if (KHBD.Context.IsCancellationRequested(request.CancellationToken) || group.KeyStateDictionary[key] == CacheGroup.State.Failed) {
                            onComplete?.Invoke(null);
                            DisposeRequest(request);
                            return null;
                        } else if (group.KeyStateDictionary[key] == CacheGroup.State.Cancelled) { // 其它请求取消时
                            group.KeyStateDictionary[key] = CacheGroup.State.Loading;
                            break;
                        }
                    }
                } else {
                    break;
                }
            }

            var opHandle = Addressables.InstantiateAsync(key, parent, false, false);
            var operateResult = await OperateHandle(request, opHandle);
            switch (operateResult) {
                case OperateResult.Succeed:
                    var result = opHandle.Result;
                    m_InstantiatedDictionary[result] = opHandle;
                    if (isRequireCache) {
                        result.SetActive(false);
                        m_CacheGameObjectDictionary.Add(key, result);
                        m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group);
                        group.KeyStateDictionary[key] = CacheGroup.State.Completed;
                        result = GetInstantiateCache(key, hasParent, originParent, isActive);
                    } else {
                        result.SetActive(isActive);
                    }
                    onComplete?.Invoke(result);
                    DisposeRequest(request);
                    return result;
                case OperateResult.Failed:
                    if (isRequireCache) {
                        m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group);
                        group.KeyStateDictionary[key] = CacheGroup.State.Failed;
                    }
                    onComplete?.Invoke(null);
                    DisposeRequest(null);
                    return null;
                case OperateResult.Cancelled:
                    if (isRequireCache) {
                        m_CacheGroupDictionary.TryGetValue(cacheGroup, out var group);
                        group.KeyStateDictionary[key] = CacheGroup.State.Cancelled;
                    }
                    onComplete?.Invoke(null);
                    DisposeRequest(null);
                    return null;
                default:
                    throw new NotSupportedException();
            }
        }

        private GameObject GetInstantiateCache(string key, bool hasParent, Transform parent, bool isActive) {
            if (hasParent && parent == null) {
                // 这种情况发生在，从加载进入时，有提供父级容器，
                // 但异步执行加载后，到实例化时，父级容器已销毁
                return null;
            }
            if (m_CacheGameObjectDictionary.TryGetValue(key, out var cache)) {
                if (!m_CacheGameObjectStackDictionary.TryGetValue(key, out var stack)) {
                    stack = m_CacheGameObjectStackDictionary[key] = new();
                }
                if (!stack.TryPop(out var result)) {
                    if (cache != null) {
                        result = UnityEngine.Object.Instantiate(cache, parent);
                    } else {
                        throw new MissingReferenceException($"The object of type 'UnityEngine.GameObject' has been destroyed but you are still trying to access it. {nameof(key)}={key}");
                    }
                } else {
                    result.transform.SetParent(parent);
                    if (isActive) {
                        result.SetActive(true);
                    }
                }
                m_CacheGameObjectMap.Add(result, key);
                if (!m_CacheGameObjectInstanceDictionary.TryGetValue(key, out var instanceSet)) {
                    instanceSet = m_CacheGameObjectInstanceDictionary[key] = new HashSet<GameObject>();
                }
                instanceSet.Add(result);
                result.SetActive(isActive);
                return result;
            }
            return null;
        }

        public async UniTask<GameObject> Instantiate(IAssetRequest request, IAssetInstantiateParent assetInstantiateParent, bool isActive = true, Action<GameObject> onComplete = null, string cacheGroup = null) {
            Assert.IsNotNull(assetInstantiateParent);

            return await Instantiate(request, assetInstantiateParent.Transform, isActive, onComplete, cacheGroup);
        }

        public async UniTask<GameObject> Instantiate(IAssetRequest request, bool isActive = true, Action<GameObject> onComplete = null, string cacheGroup = null) {
            return await Instantiate(request, parent: null, isActive, onComplete, cacheGroup);
        }

        public async UniTask<bool> Download<T>(IAssetRequest request, Action<bool> onComplete = null) {
            Assert.IsNotNull(request);

            request.DownloadProgress?.SetProgress(0);

            if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                onComplete?.Invoke(false);
                return false;
            }

            var key = Validate(request.Key);
            if (string.IsNullOrEmpty(key)) {
                Debug.LogWarning("请求空资产键");
                return false;
            }
            var opHandle = Addressables.LoadAssetAsync<T>(key);
            var operateResult = await OperateHandle(request, opHandle);
            if (operateResult == OperateResult.Succeed) {
                Addressables.Release(opHandle);
                onComplete?.Invoke(true);
                return true;
            } else {
                onComplete?.Invoke(false);
                return false;
            }
        }

        public async UniTask<bool> DownloadDependencies(IAssetRequest request, Action<bool> onComplete = null) {
            Assert.IsNotNull(request);

            request.DownloadProgress?.SetProgress(0);

            if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                onComplete?.Invoke(false);
                return false;
            }

            var key = Validate(request.Key);
            if (string.IsNullOrEmpty(key)) {
                Debug.LogWarning("请求空资产键");
                return false;
            }
            var opHandle = Addressables.DownloadDependenciesAsync(key);
            var operateResult = await OperateHandle(request, opHandle);
            if (operateResult == OperateResult.Succeed) {
                Addressables.Release(opHandle);
                onComplete?.Invoke(true);
                return true;
            } else {
                onComplete?.Invoke(false);
                return false;
            }
        }

        public void Release(AssetHandle handle) {
            Assert.IsTrue(handle.IsNotNone());
            if (KHBD.Context.IsApplicationQuit) {
                return;
            }
            if (m_CacheMap.TryGetValue(handle, out var cache)) {
                m_CacheMap.Remove(handle);
                m_CacheInstanceSetDictionary[cache].Remove(handle);
            } else if (m_Dictionary.TryGetValue(handle, out var record)) {
                m_Dictionary.Remove(handle);
                Addressables.Release(record.AsyncOperationHandle);
            }
        }

        public void Release(GameObject gameObject) {
            Assert.IsNotNull(gameObject);
            if (KHBD.Context.IsApplicationQuit) {
                return;
            }
            if (m_CacheGameObjectMap.TryGetValue(gameObject, out var key)) {
                m_CacheGameObjectMap.Remove(gameObject);
                m_CacheGameObjectInstanceDictionary[key].Remove(gameObject);
                if (m_CacheGameObjectStackDictionary.TryGetValue(key, out var stack)) {
                    gameObject.SetActive(false);
                    gameObject.transform.SetParent(m_CacheTransform);
                    stack.Push(gameObject);
                } else {
                    UnityEngine.Object.Destroy(gameObject);
                }
            } else if (m_InstantiatedDictionary.TryGetValue(gameObject, out var opHandle)) {
                m_InstantiatedDictionary.Remove(gameObject);
                if (!opHandle.IsValid()) {
                    UnityEngine.Debug.LogWarning($"释放资产时，状态非法 {nameof(gameObject)}={gameObject.name}");
                }
                Addressables.Release(opHandle);
            }
        }

        private enum OperateResult { Succeed, Failed, Cancelled }
        private async UniTask<OperateResult> OperateHandle(IAssetRequest request, AsyncOperationHandle opHandle) {
            if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                return OperateResult.Cancelled;
            }

            await KHBD.Context.WaitUntil(() => {
                var condition = !opHandle.IsDone && !KHBD.Context.IsCancellationRequested(request.CancellationToken);
                if (condition) {
                    request.DownloadProgress?.SetProgress(opHandle.PercentComplete);
                }
                return !condition;
            }, request.CancellationToken);
            if (KHBD.Context.IsCancellationRequested(request.CancellationToken)) {
                Addressables.Release(opHandle);
                return OperateResult.Cancelled;
            } else if (opHandle.Status != AsyncOperationStatus.Succeeded) {
                Addressables.Release(opHandle);
                LogDownloadWarning(request);
                return OperateResult.Failed;
            } else if (opHandle.Result == null) {
                Addressables.Release(opHandle);
                return OperateResult.Cancelled;
            }
            request.DownloadProgress?.SetProgress(1);
            return OperateResult.Succeed;
        }

        private static void LogDownloadWarning(IAssetRequest request) {
            if (!request.IgnoreFailureLog) {
                Debug.LogWarning($"<color=#FF0032>下载失败</color>，资产名称 [ <color=#00DEFF>{request.Key}</color> ]");
            }
        }

        private string Validate(string key) {
            var result = key.Trim();
            if (result.Length != key.Length) {
                Debug.LogError($"[${nameof(IAssetService)}.{nameof(Validate)}()] 警告！资产加载时发现传入的key中首或尾存在空格");
                return result;
            } else {
                return key;
            }
        }

        string IAssetServiceInternal.GetKey(AssetHandle handle) {
            Assert.IsTrue(handle.IsNotNone());

            if (m_CacheMap.TryGetValue(handle, out var cache)) {
                handle = cache;
            }
            if (m_Dictionary.TryGetValue(handle, out var record)) {
                return record.Key;
            } else {
                throw new InvalidProgramException();
            }
        }

        T IAssetServiceInternal.GetValue<T>(AssetHandle<T> handle) {
            Assert.IsTrue(handle != AssetHandle.None);

            if (m_CacheMap.TryGetValue(handle, out var cache)) {
                handle = cache;
            }
            if (m_Dictionary.TryGetValue(handle, out var record)) {
                return record.AsyncOperationHandle.Convert<T>().Result;
            } else {
                throw new InvalidProgramException();
            }
        }
    }
}

namespace Internal.KuiHuaBaoDian.Services.Asset {

    public interface IAssetServiceInternal {
        void ClearCache();
        string GetKey(AssetHandle handle);

        T GetValue<T>(AssetHandle<T> handle) where T : UnityEngine.Object;
    }
}
